﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Linq.Dynamic;

namespace More.Engine.Query
{
    public class InqlQuery
    {
        private IDictionary<InqlExpressionType, string> _expressions = new Dictionary<InqlExpressionType, string>();
        private IDictionary<string, object> _variables;

        public IDictionary<string, object> Variables
        {
            get { return _variables ?? (_variables = new Dictionary<string, object>()); }
            set { _variables = value; }
        }

        public IDictionary<InqlExpressionType, string> Expressions
        {
            get { return _expressions; }
            set { _expressions = value; }
        }

        public IQueryable<T> Filter<T>(IQueryable<T> source)
        {
            IQueryable<T> result = source;
            foreach (var expression in Expressions)
            {
                result = FilterExpression<T>(result, expression.Key, expression.Value);
            }
            return result;
        }

        private IQueryable<T> FilterExpression<T>(IQueryable<T> result, InqlExpressionType key, string expression)
        {
            object[] values = null;
            switch (key)
            {
                case InqlExpressionType.Where:
                    return result.Where(ProcessExpressionVariables(expression, out values), values);
                case InqlExpressionType.Take:
                    return result.Take(Convert.ToInt32(expression));
                case InqlExpressionType.Skip:
                    return result.Skip(Convert.ToInt32(expression));
                case InqlExpressionType.OrderBy:
                    return result.OrderBy(ProcessExpressionVariables(expression, out values), values);
                case InqlExpressionType.OrderByDescending:
                    return result.OrderBy(expression + " descending");
            }
            return result;
        }

        protected virtual string ProcessExpressionVariables(string expression, out object[] values)
        {
            string strRegex = @"\$(?<varname>[\w\-]+)";
            Regex myRegex = new Regex(strRegex, RegexOptions.None);
            var list = new List<object>();
            var i = 0;
            var result = myRegex.Replace(expression, (m) =>
            {
                var varName = m.Groups["varname"].Value;

                if (Variables != null && Variables.ContainsKey(varName))
                {
                    list.Add(Variables[varName]);
                }
                else
                {
                    throw new Exception(string.Format("Variable '{0}' was not found.", varName));
                }

                var resultString = "@" + i;
                i++;
                return resultString;
            });
            values = list.ToArray();
            return result;
        }
    }
}
